/*
 * Copyright 2010 - 2011, The PLDesktop Development Team
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#ifndef PLATOMIC_H
#define PLATOMIC_H

#include <cstddef>
using namespace std;
#include <type_traits>

template<typename T, bool bAllow = (std::is_integral<T>::value | std::is_pointer<T>::value)>
class Atomic;

/**
 * Wrapper around gcc's builtin atomic operations.
 */
template<typename T>
class Atomic<T, true> {
public:
	/**
	 * The constructor.
	 *
	 * @param value initial value.
	 */
	inline Atomic(T value = T()) : m_atom(value) {}
	/**
	 * The copy-constructor.
	 *
	 * @param x reference object.
	 */
	inline Atomic(const Atomic &x) : m_atom(x.m_atom) {}
	/**
	 * The assignment operator.
	 *
	 * @param x reference object.
	 * @return this.
	 */
	inline Atomic &operator = (const Atomic &x) {
		m_atom = x.m_atom;
		return *this;
	}
	/**
	 * The destructor does nothing.
	 */
	inline virtual ~Atomic() {}
	/**
	 * Addition.
	 *
	 * @param x value to add.
	 * @return the value after the addition.
	 */
	inline T operator += (T x) {
		m_atom += x;
		return m_atom;
	}
	/**
	 * Subtraction.
	 *
	 * @param x value to subtract.
	 * @return the value after the subtraction.
	 */
	inline T operator -= (T x) {
		m_atom -= x;
		return m_atom;
	}
	/**
	 * Bitwise or.
	 *
	 * @param x the operand.
	 * @return the value after the bitwise or.
	 */
	inline T operator |= (T x) {
		m_atom |= x;
		return m_atom;
	}
	/**
	 * Bitwise and.
	 *
	 * @param x the operand.
	 * @return the value after the bitwise and.
	 */
	inline T operator &= (T x) {
		m_atom &= x;
		return m_atom;
	}
	/**
	 * Bitwise xor.
	 *
	 * @param x the operand.
	 * @return the value after the bitwise xor.
	 */
	inline T operator ^= (T x) {
		m_atom ^= x;
		return m_atom;
	}
	/**
	 * Compare and swap.
	 *
	 * @param oldVal the comparision value.
	 * @param newVal the new value.
	 * @return true, if the Atomic had the value oldVal and the value was
	 * changed to newVal, false otherwise.
	 */
	inline bool compareAndSwap(T oldVal, T newVal) {
		if(m_atom == oldVal) {
			m_atom = newVal;
			return true;
		}
		return false;
	}
	/**
	 * Get the value.
	 *
	 * @return the value of the Atomic.
	 */
	inline operator T () const {
		return m_atom;
	}
private:
	volatile T m_atom;
};

/**
 * Wrapper around gcc's builtin atomic operations.
 */
template<>
class Atomic<bool, true> : public Atomic<size_t> {
public:
	/**
	 * The constructor.
	 *
	 * @param value initial value.
	 */
	inline Atomic(bool value = false) : Atomic<size_t>((value) ? 1 : 0) {}
	/**
	 * The copy-constructor.
	 *
	 * @param x reference object.
	 */
	inline Atomic(const Atomic &x) : Atomic<size_t>(x) {}
	/**
	 * The assignment operator.
	 *
	 * @param x reference object.
	 * @return this.
	 */
	inline Atomic &operator = (const Atomic &x) {
		Atomic<size_t>::operator = (x);
		return *this;
	}
	/**
	 * The destructor does nothing.
	 */
	inline ~Atomic() {}
	/**
	 * Bitwise or.
	 *
	 * @param x the operand.
	 * @return the value after the bitwise or.
	 */
	inline bool operator |= (bool x) {
		return Atomic<size_t>::operator |= (x);
	}
	/**
	 * Bitwise and.
	 *
	 * @param x the operand.
	 * @return the value after the bitwise and.
	 */
	inline bool operator &= (bool x) {
		return Atomic<size_t>::operator &= (x);
	}
	/**
	 * Bitwise xor.
	 *
	 * @param x the operand.
	 * @return the value after the bitwise xor.
	 */
	inline bool operator ^= (bool x) {
		return Atomic<size_t>::operator ^= (x);
	}
	/**
	 * Compare and swap.
	 *
	 * @param oldVal the comparision value.
	 * @param newVal the new value.
	 * @return true, if the Atomic had the value oldVal and the value was
	 * changed to newVal, false otherwise.
	 */
	inline bool compareAndSwap(bool oldVal, bool newVal) {
		return Atomic<size_t>::compareAndSwap((oldVal) ? 1 : 0, (newVal) ? 1 : 0);
	}
	/**
	 * Get the value.
	 *
	 * @return the value of the Atomic.
	 */
	inline operator bool () const {
		return (Atomic<size_t>::operator size_t() == 1) ? true : false;
	}
};

#endif
